%%%----------------------------------------------------------------------
%%% File    : mod_adhoc.erl
%%% Author  : Magnus Henoch <henoch@dtek.chalmers.se>
%%% Purpose : Handle incoming ad-doc requests (JEP-0050)
%%% Created : 15 Nov 2005 by Magnus Henoch <henoch@dtek.chalmers.se>
%%% Id      : $Id: mod_adhoc.erl 486 2006-01-19 02:17:31Z alexey $
%%%----------------------------------------------------------------------

-module(mod_adhoc).
-author('henoch@dtek.chalmers.se').
-vsn('$Revision: 486 $ ').

-behaviour(gen_mod).

-export([start/2,
	 stop/1,
	 process_local_iq/3,
	 process_sm_iq/3,
	 get_local_commands/5,
	 get_local_identity/5,
	 get_local_features/5,
	 get_sm_commands/5,
	 get_sm_identity/5,
	 get_sm_features/5,
	 ping_item/4,
	 ping_command/4]).

-include("ejabberd.hrl").
-include("jlib.hrl").
-include("adhoc.hrl").

start(Host, Opts) ->
    IQDisc = gen_mod:get_opt(iqdisc, Opts, one_queue),

    gen_iq_handler:add_iq_handler(ejabberd_local, Host, ?NS_COMMANDS,
				  ?MODULE, process_local_iq, IQDisc),
    gen_iq_handler:add_iq_handler(ejabberd_sm, Host, ?NS_COMMANDS,
				  ?MODULE, process_sm_iq, IQDisc),
    
    ejabberd_hooks:add(disco_local_identity, Host, ?MODULE, get_local_identity, 99),
    ejabberd_hooks:add(disco_local_features, Host, ?MODULE, get_local_features, 99),
    ejabberd_hooks:add(disco_local_items, Host, ?MODULE, get_local_commands, 99),
    ejabberd_hooks:add(disco_sm_identity, Host, ?MODULE, get_sm_identity, 99),
    ejabberd_hooks:add(disco_sm_features, Host, ?MODULE, get_sm_features, 99),
    ejabberd_hooks:add(disco_sm_items, Host, ?MODULE, get_sm_commands, 99),
    ejabberd_hooks:add(adhoc_local_items, Host, ?MODULE, ping_item, 100),
    ejabberd_hooks:add(adhoc_local_commands, Host, ?MODULE, ping_command, 100).

stop(Host) ->
    ejabberd_hooks:delete(adhoc_local_commands, Host, ?MODULE, ping_command, 100),
    ejabberd_hooks:delete(adhoc_local_items, Host, ?MODULE, ping_item, 100),
    ejabberd_hooks:delete(disco_sm_items, Host, ?MODULE, get_sm_commands, 99),
    ejabberd_hooks:delete(disco_sm_features, Host, ?MODULE, get_sm_features, 99),
    ejabberd_hooks:delete(disco_sm_identity, Host, ?MODULE, get_sm_identity, 99),
    ejabberd_hooks:delete(disco_local_items, Host, ?MODULE, get_local_commands, 99),
    ejabberd_hooks:delete(disco_local_features, Host, ?MODULE, get_local_features, 99),
    ejabberd_hooks:delete(disco_local_identity, Host, ?MODULE, get_local_identity, 99),

    gen_iq_handler:remove_iq_handler(ejabberd_sm, Host, ?NS_COMMANDS),
    gen_iq_handler:remove_iq_handler(ejabberd_local, Host, ?NS_COMMANDS).

%-------------------------------------------------------------------------

get_local_commands(Acc, _From, #jid{server = Server, lserver = LServer} = _To, "", Lang) ->
    Display = gen_mod:get_module_opt(LServer, ?MODULE, report_commands_node, false),
    case Display of
	false ->
	    Acc;
	_ ->
	    Items = case Acc of
			{result, I} -> I;
			_ -> []
		    end,
	    Nodes = [{xmlelement,
		      "item",
		      [{"jid", Server},
		       {"node", ?NS_COMMANDS},
		       {"name", translate:translate(Lang, "Commands")}],
		      []}],
	    {result, Items ++ Nodes}
    end;

get_local_commands(_Acc, From, #jid{lserver = LServer} = To, ?NS_COMMANDS, Lang) ->
    ejabberd_hooks:run_fold(adhoc_local_items, LServer, {result, []}, [From, To, Lang]);

get_local_commands(_Acc, _From, _To, "ping", _Lang) ->
    {result, []};

get_local_commands(Acc, _From, _To, _Node, _Lang) ->
    Acc.

%-------------------------------------------------------------------------

get_sm_commands(Acc, _From, #jid{lserver = LServer} = To, "", Lang) ->
    Display = gen_mod:get_module_opt(LServer, ?MODULE, report_commands_node, false),
    case Display of
	false ->
	    Acc;
	_ ->
	    Items = case Acc of
			{result, I} -> I;
			_ -> []
		    end,
	    Nodes = [{xmlelement,
		      "item",
		      [{"jid", jlib:jid_to_string(To)},
		       {"node", ?NS_COMMANDS},
		       {"name", translate:translate(Lang, "Commands")}],
		      []}],
	    {result, Items ++ Nodes}
    end;

get_sm_commands(_Acc, From, #jid{lserver = LServer} = To, ?NS_COMMANDS, Lang) ->
    ejabberd_hooks:run_fold(adhoc_sm_items, LServer, {result, []}, [From, To, Lang]);

get_sm_commands(Acc, _From, _To, _Node, _Lang) ->
    Acc.

%-------------------------------------------------------------------------

%% On disco info request to the ad-hoc node, return automation/command-list.
get_local_identity(Acc, _From, _To, ?NS_COMMANDS, Lang) ->
    [{xmlelement, "identity",
      [{"category", "automation"},
       {"type", "command-list"},
       {"name", translate:translate(Lang, "Commands")}], []} | Acc];

get_local_identity(Acc, _From, _To, "ping", Lang) ->
    [{xmlelement, "identity",
      [{"category", "automation"},
       {"type", "command-node"},
       {"name", translate:translate(Lang, "Ping")}], []} | Acc];

get_local_identity(Acc, _From, _To, _Node, _Lang) ->
    Acc.

%-------------------------------------------------------------------------

%% On disco info request to the ad-hoc node, return automation/command-list.
get_sm_identity(Acc, _From, _To, ?NS_COMMANDS, Lang) ->
    [{xmlelement, "identity",
      [{"category", "automation"},
       {"type", "command-list"},
       {"name", translate:translate(Lang, "Commands")}], []} | Acc];

get_sm_identity(Acc, _From, _To, _Node, _Lang) ->
    Acc.

%-------------------------------------------------------------------------

get_local_features(Acc, _From, _To, "", _Lang) ->
    Feats = case Acc of
		{result, I} -> I;
		_ -> []
	    end,
    {result, Feats ++ [?NS_COMMANDS]};

get_local_features(_Acc, _From, _To, ?NS_COMMANDS, _Lang) ->
    %% override all lesser features...
    {result, []};

get_local_features(_Acc, _From, _To, "ping", _Lang) ->
    %% override all lesser features...
    {result, [?NS_COMMANDS]};

get_local_features(Acc, _From, _To, _Node, _Lang) ->
    Acc.

%-------------------------------------------------------------------------

get_sm_features(Acc, _From, _To, "", _Lang) ->
    Feats = case Acc of
		{result, I} -> I;
		_ -> []
	    end,
    {result, Feats ++ [?NS_COMMANDS]};

get_sm_features(_Acc, _From, _To, ?NS_COMMANDS, _Lang) ->
    %% override all lesser features...
    {result, []};

get_sm_features(Acc, _From, _To, _Node, _Lang) ->
    Acc.

%-------------------------------------------------------------------------

process_local_iq(From, To, IQ) ->
    process_adhoc_request(From, To, IQ, adhoc_local_commands).


process_sm_iq(From, To, IQ) ->
    process_adhoc_request(From, To, IQ, adhoc_sm_commands).


process_adhoc_request(From, To, #iq{sub_el = SubEl} = IQ, Hook) ->
    ?DEBUG("About to parse ~p...", [IQ]),
    case adhoc:parse_request(IQ) of
	{error, Error} ->
	    IQ#iq{type = error, sub_el = [SubEl, Error]};
	#adhoc_request{} = AdhocRequest ->
	    Host = To#jid.lserver,
	    case ejabberd_hooks:run_fold(Hook, Host, empty,
					 [From, To, AdhocRequest]) of
		ignore ->
		    ignore;
		empty ->
		    IQ#iq{type = error, sub_el = [SubEl, ?ERR_ITEM_NOT_FOUND]};
		{error, Error} ->
		    IQ#iq{type = error, sub_el = [SubEl, Error]};
		Command ->
		    IQ#iq{type = result, sub_el = [Command]}
	    end
    end.


ping_item(Acc, _From, #jid{server = Server} = _To, Lang) ->
    Items = case Acc of
		{result, I} ->
		    I;
		_ ->
		    []
	    end,
    Nodes = [{xmlelement, "item",
	      [{"jid", Server},
	       {"node", "ping"},
	       {"name", translate:translate(Lang, "Ping")}],
	      []}],
    {result, Items ++ Nodes}.


ping_command(_Acc, _From, _To,
	     #adhoc_request{lang = Lang,
			    node = "ping",
			    sessionid = _Sessionid,
			    action = Action} = Request) ->
    if 
	Action == ""; Action == "execute" ->
	    adhoc:produce_response(
	      Request,
	      #adhoc_response{status = completed,
			      notes = [{"info", translate:translate(
						  Lang,
						  "Pong")}]});
	true ->
	    {error, ?ERR_BAD_REQUEST}
    end;

ping_command(Acc, _From, _To, _Request) ->
    Acc.

